Completed
Push — master ( d0979c...22246f )
by Sander
49s
created

background.js ➔ ... ➔ API.tabs.then   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        //return true;
53
        try {
54
            PAPI.decryptString(testMasterPasswordAgainst, password);
55
            return true;
56
        } catch (e) {
57
            return false;
58
        }
59
    }
60
61
    _self.isMasterPasswordValid = isMasterPasswordValid;
62
63
64
    var local_credentials = [];
65
    var local_vault = [];
66
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
67
    _self.settings = {};
68
    _self.ticker = null;
69
    _self.running = false;
70
    function getSettings() {
71
72
        storage.get('settings').then(function (_settings) {
73
74
            if ((!_settings || !_settings.hasOwnProperty('nextcloud_host')) && !master_password) {
75
                API.tabs.create({
76
                    url: '/html/browser_action/browser_action.html'
77
                });
78
79
                return;
80
            }
81
82
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
83
                _self.settings.isInstalled = 1;
84
                testMasterPasswordAgainst = _settings.nextcloud_username;
85
                return;
86
            }
87
88
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
89
                var field = encryptedFieldSettings[i];
90
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
91
            }
92
93
94
            _self.settings = _settings;
95
96
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
97
                _self.settings.ignored_sites = [];
98
            }
99
100
            if (!_self.settings.hasOwnProperty('disable_browser_autofill')) {
101
                _self.settings.disable_browser_autofill = true;
102
            }
103
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
104
                _self.settings.no_results_found_tab = 'list';
105
            }
106
107
108
            PAPI.host = _settings.nextcloud_host;
109
            PAPI.username = _settings.nextcloud_username;
110
            PAPI.password = _settings.nextcloud_password;
111
            if (!_settings.vault_password) {
112
                return;
113
            }
114
            if (PAPI.credentialsSet()) {
115
                getCredentials();
116
                if (_self.running) {
117
                    clearInterval(_self.ticker);
118
                }
119
120
                _self.running = true;
121
                _self.ticker = setInterval(function () {
122
                    getCredentials();
123
                }, _self.settings.refreshTime * 1000);
124
            } else {
125
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
126
            }
127
        });
128
    }
129
130
    _self.getSettings = getSettings;
131
132
    function getRuntimeSettings() {
133
        return _self.settings;
134
    }
135
136
    _self.getRuntimeSettings = getRuntimeSettings;
137
138
    function getSetting(name) {
139
        return _self.settings[name];
140
    }
141
142
    _self.getSetting = getSetting;
143
144
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
145
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
146
            var field = encryptedFieldSettings[i];
147
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
148
        }
149
        PAPI.host = settings.nextcloud_host;
150
        PAPI.username = settings.nextcloud_username;
151
        PAPI.password = settings.nextcloud_password;
152
153
        if (!settings.hasOwnProperty('ignored_sites')) {
154
            settings.ignored_sites = [];
155
        }
156
157
        if (!settings.hasOwnProperty('disable_browser_autofill')) {
158
            settings.disable_browser_autofill = true;
159
        }
160
161
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
162
            _self.settings.disable_browser_autofill = 'list';
163
        }
164
165
        //window.settings contains the run-time settings
166
        _self.settings = settings;
167
168
169
        storage.set('settings', settings).then(function () {
170
            getSettings();
171
        });
172
173
    }
174
175
    _self.saveSettings = saveSettings;
176
177
178
    function getCredentials() {
179
        if (!master_password) {
180
            return;
181
        }
182
        //console.log('Loading vault with the following settings: ', settings);
183
        var tmpList = [];
184
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
185
            if (vault.hasOwnProperty('error')) {
186
                return;
187
            }
188
            var _credentials = vault.credentials;
189
            for (var i = 0; i < _credentials.length; i++) {
190
                var key = _self.settings.vault_password;
191
                var credential = _credentials[i];
192
                if (credential.hidden === 1) {
193
                    continue;
194
                }
195
                var usedKey = key;
196
                //Shared credentials are not implemented yet
197
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
198
                    usedKey = PAPI.decryptString(credential.shared_key, key);
199
200
                }
201
                credential = PAPI.decryptCredential(credential, usedKey);
202
                if (credential.delete_time === 0) {
203
                    tmpList.push(credential);
204
                }
205
206
            }
207
            delete vault.credentials;
208
            local_vault = vault;
209
            local_credentials = tmpList;
210
            getSharedCredentials();
211
212
        });
213
    }
214
215
    _self.getCredentials = getCredentials;
216
217
    function getSharedCredentials() {
218
        PAPI.getCredendialsSharedWithUs(_self.settings.default_vault.guid, function (credentials) {
219
            for (var i = 0; i < credentials.length; i++) {
220
                var _shared_credential = credentials[i];
221
                var _shared_credential_data;
222
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, _self.settings.vault_password);
223
                try {
224
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
225
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
226
227
                }
228
                if (_shared_credential_data) {
229
                    delete _shared_credential.credential_data;
230
                    _shared_credential_data.acl = _shared_credential;
231
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
232
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
233
                    local_credentials.push(_shared_credential_data);
234
                }
235
            }
236
            updateTabsIcon();
237
        });
238
    }
239
240
241
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
242
        if (!master_password) {
243
            return [];
244
        }
245
        if (!_url || _url === '') {
246
            return [];
247
        }
248
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
249
        var found_list = [];
250
        for (var i = 0; i < local_credentials.length; i++) {
251
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
252
                if (local_credentials[i].url.indexOf(url) !== -1) {
253
                    found_list.push(local_credentials[i]);
254
                }
255
            }
256
        }
257
        return found_list;
258
    }
259
260
    _self.getCredentialsByUrl = getCredentialsByUrl;
261
262
263
    function saveCredential(credential) {
264
        //@TODO save shared password
265
        if (credential.shared_key) {
266
            return;
267
        }
268
        if (!credential.credential_id) {
269
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
270
                local_credentials.push(createdCredential);
271
            });
272
        } else {
273
            var credential_index;
274
            for (var i = 0; i < local_credentials.length; i++) {
275
                if (local_credentials[i].guid === credential.guid) {
276
                    credential_index = i;
277
                    break;
278
                }
279
            }
280
281
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
282
                if (credential_index) {
283
                    local_credentials[credential_index] = updatedCredential;
284
                }
285
            });
286
        }
287
    }
288
289
    _self.saveCredential = saveCredential;
290
291
    function getCredentialByGuid(guid) {
292
        for (var i = 0; i < local_credentials.length; i++) {
293
            var credential = local_credentials[i];
294
            if (credential.guid === guid) {
295
                return credential;
296
            }
297
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
298
    }
299
300
    _self.getCredentialByGuid = getCredentialByGuid;
301
302
    function getCredentialForHTTPAuth(req) {
303
        return getCredentialsByUrl(req.url)[0];
304
    }
305
306
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
307
308
    var mined_data = [];
309
310
    function minedForm(data, sender) {
311
        var url = sender.url;
312
        var existingLogins = getCredentialsByUrl(sender.url);
313
        var title = API.i18n.getMessage('detected_new_login') + ':';
314
        var minedMatchingID = null;
315
        for (var j = 0; j < existingLogins.length; j++) {
316
            var login = existingLogins[j];
317
            if (login.username === data.username) {
318
                if (login.password !== data.password) {
319
                    minedMatchingID = login.guid;
320
                    title = API.i18n.getMessage('detected_changed_login') + ':';
321
                }
322
                else {
323
                    //console.log('No changes detected');
324
                    delete mined_data[sender.tab.id];
325
                    return;
326
                }
327
            }
328
        }
329
        mined_data[sender.tab.id] = {
330
            title: title,
331
            url: url,
332
            username: data.username,
333
            password: data.password,
334
            label: sender.title,
335
            guid: minedMatchingID
336
        };
337
338
        //console.log('Done mining, ', mined_data, sender.tab.id);
339
    }
340
341
    _self.minedForm = minedForm;
342
343
    function getMinedData(args, sender) {
344
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
345
        var senderUrl = sender.tab.url;
346
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
347
        if (!_self.settings) {
348
            return null;
349
        }
350
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
351
            return mined_data[sender.tab.id];
352
        }
353
        var matches = _self.settings.ignored_sites.filter(function (item) {
354
            return typeof item === 'string' && site.indexOf(item) > -1;
355
        });
356
357
        if (matches.length !== 0) {
358
            return null;
359
        }
360
        return mined_data[sender.tab.id];
361
    }
362
363
    _self.getMinedData = getMinedData;
364
365
    function clearMined(args, sender) {
366
        delete mined_data[sender.tab.id];
367
    }
368
369
    _self.clearMined = clearMined;
370
371
    function saveMinedCallback(args) {
372
        createIconForTab(args.sender.tab);
373
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
374
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
375
            });
376
        });
377
    }
378
379
    function ignoreSite(_url) {
380
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
381
            _self.settings.ignored_sites = [];
382
        }
383
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
384
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
385
            _self.settings.ignored_sites.push(site);
386
            saveSettings(_self.settings);
387
        }
388
    }
389
390
    _self.ignoreSite = ignoreSite;
391
392
    function passToParent(args, sender) {
393
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
394
        });
395
    }
396
397
    _self.passToParent = passToParent;
398
399
    function getActiveTab(opt) {
400
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
401
            var tab = tabs[0];
402
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
403
            });
404
        });
405
    }
406
407
    _self.getActiveTab = getActiveTab;
408
409
    function updateCredentialUrlDoorhanger(login) {
410
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
411
            var tab = tabs[0];
412
            var data = login;
413
            data.url = tab.url;
414
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
415
            API.tabs.sendMessage(tab.id, {
416
                method: 'showUrlUpdateDoorhanger',
417
                args: {data: data}
418
            });
419
        });
420
    }
421
422
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
423
424
    function updateCredentialUrl(data, sender) {
425
        mined_data[sender.tab.id] = data;
426
        saveMined({}, sender);
427
428
    }
429
430
    _self.updateCredentialUrl = updateCredentialUrl;
431
432
    function saveMined(args, sender) {
433
        var data = mined_data[sender.tab.id];
434
        var credential,
435
            credential_index;
436
437
        if (data.guid === null) {
438
            credential = PAPI.newCredential();
439
        } else {
440
            for (var i = 0; i < local_credentials.length; i++) {
441
                if (local_credentials[i].guid === data.guid) {
442
                    credential = local_credentials[i];
443
                    credential_index = i;
444
                    break;
445
                }
446
            }
447
        }
448
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
449
        credential.password = data.password;
450
        credential.url = sender.tab.url;
451
        if (credential.guid !== null) {
452
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
453
                if (credential_index) {
454
                    local_credentials[credential_index] = updatedCredential;
455
                }
456
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
457
                delete mined_data[sender.tab.id];
458
            });
459
        } else {
460
            credential.label = sender.tab.title;
461
            credential.vault_id = local_vault.vault_id;
462
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
463
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
464
                local_credentials.push(createdCredential);
465
                delete mined_data[sender.tab.id];
466
            });
467
        }
468
    }
469
470
    _self.saveMined = saveMined;
471
472
    function searchCredential(searchText) {
473
        searchText = searchText.toLowerCase();
474
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
475
        var results = [];
476
        for (var i = 0; i < local_credentials.length; i++) {
477
            var credential = local_credentials[i];
478
            for (var f = 0; f < searchFields.length; f++) {
479
                var field = searchFields[f];
480
                if (!credential[field]) {
481
                    continue;
482
                }
483
484
                var field_value = credential[field].toLowerCase();
485
                if (field_value.indexOf(searchText) !== -1) {
486
                    results.push(credential);
487
                    break;
488
                }
489
            }
490
        }
491
        return results;
492
    }
493
494
    _self.searchCredential = searchCredential;
495
496
497
    function injectCreateCredential(args, sender) {
498
        var credential = PAPI.newCredential();
499
        credential.label = args.label;
500
        credential.username = args.username;
501
        credential.password = args.password;
502
        credential.vault_id = local_vault.vault_id;
503
        credential.url = sender.tab.url;
504
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
505
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
506
            local_credentials.push(createdCredential);
507
508
        });
509
    }
510
511
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
512
513
    function isVaultKeySet() {
514
        return (_self.settings.vault_password !== null);
515
    }
516
517
    _self.isVaultKeySet = isVaultKeySet;
518
519
    function isAutoFillEnabled() {
520
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
521
            return true;
522
        }
523
        return (_self.settings.disableAutoFill === false);
524
    }
525
526
    _self.isAutoFillEnabled = isAutoFillEnabled;
527
528
    var doorhangerData = null;
529
530
    function setDoorhangerData(data) {
531
        doorhangerData = data;
532
    }
533
534
    _self.setDoorhangerData = setDoorhangerData;
535
536
    function getDoorhangerData() {
537
        return doorhangerData;
538
    }
539
540
    _self.getDoorhangerData = getDoorhangerData;
541
542
    function closeSetupTab() {
543
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
544
            if (tabs) {
545
                API.tabs.remove(tabs[0].id);
546
            }
547
        });
548
    }
549
550
    _self.closeSetupTab = closeSetupTab;
551
552
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
553
554
        if (!msg || !msg.hasOwnProperty('method')) {
555
            return;
556
        }
557
        var result = false;
558
        if (_self[msg.method]) {
559
            result = _self[msg.method](msg.args, sender);
560
        } else {
561
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
562
        }
563
564
        sendResponse(result);
565
    });
566
567
    var defaultColor = '#0082c9';
568
569
    function createIconForTab(tab) {
570
        if (!master_password) {
571
            return;
572
        }
573
        var tabUrl = tab.url;
574
        var logins = getCredentialsByUrl(tabUrl);
575
        if (tab.active) {
576
            window.contextMenu.setContextItems(logins);
577
        }
578
        var credentialAmount = logins.length;
579
        API.browserAction.setBadgeText({
580
            text: credentialAmount.toString(),
581
            tabId: tab.id
582
        });
583
        API.browserAction.setBadgeBackgroundColor({
584
            color: defaultColor,
585
            tabId: tab.id
586
        });
587
588
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
589
        API.browserAction.setTitle({
590
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
591
            tabId: tab.id
592
        });
593
    }
594
595
    function displayLogoutIcons() {
596
        if (_self.settings) {
597
            API.tabs.query({}).then(function (tabs) {
598
                for (var t = 0; t < tabs.length; t++) {
599
                    var tab = tabs[t];
600
                    API.browserAction.setBadgeText({
601
                        text: '🔑',
602
                        tabId: tab.id
603
                    });
604
                    API.browserAction.setBadgeBackgroundColor({
605
                        color: '#ff0000',
606
                        tabId: tab.id
607
                    });
608
                    API.browserAction.setTitle({
609
                        title: API.i18n.getMessage('browser_action_title_locked'),
610
                        tabId: tab.id
611
                    });
612
                }
613
            });
614
        }
615
    }
616
617
    function updateTabsIcon() {
618
        API.tabs.query({}).then(function (tabs) {
619
            for (var t = 0; t < tabs.length; t++) {
620
                var tab = tabs[t];
621
                createIconForTab(tab);
622
            }
623
        });
624
    }
625
626
627
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
628
        if (master_password) {
629
            createIconForTab(tab);
630
        } else {
631
            displayLogoutIcons();
632
        }
633
    });
634
635
    API.tabs.onActivated.addListener(function () {
636
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
637
            if (master_password) {
638
                createIconForTab(tabs[0]);
639
            } else {
640
                displayLogoutIcons();
641
            }
642
        });
643
    });
644
645
    displayLogoutIcons();
646
647
648
    storage.get('master_password').then(function (password) {
649
        if (password) {
650
            master_password = password;
651
            API.api.browserAction.setBadgeBackgroundColor({
652
                color: defaultColor
653
            });
654
        }
655
        getSettings();
656
    }).error(function (error) {
657
        if (error === "Data not found") {
658
            getSettings();
659
        }
660
    });
661
    return _window;
662
}());
663
664